
;
; Switches the processor to user-mode (ring 3)
;
[GLOBAL switchToUserMode]
switchToUserMode:
  mov   ax, 0x23        ; Load the new data segment descriptor with an RPL of 3.
  mov   ds, ax          ; Propagate the change to all segment registers.
  mov   es, ax
  mov   fs, ax
  mov   gs, ax
 
  sti                   ; Must STI BEFORE pushfd!
  mov   eax, esp        ; Save the stack pointer before pushing anything
  push  0x23            ; Push the new stack segment with an RPL of 3.
  push  eax             ; Push what the was ESP before pushing anything.
 
  pushfd                ; Push EFLAGS
  push 0x1B             ; Push the new code segment with an RPL of 3.
  push .usermode_return ; Push the EIP to IRET to.
 
  iret

.usermode_return:
  ret

[GLOBAL writeMsr]
writeMsr:
  mov edx, [esp+8]
  mov eax, [esp+12]
  mov ecx, [esp+4]
  wrmsr
  ret

[GLOBAL writeStackPointer]
writeStackPointer:
  pop ebx
  pop eax
  mov esp, eax
  jmp ebx

[GLOBAL readStackPointer]
readStackPointer:
  mov eax, esp
  add eax, 4          ; Stack was pushed with return address, so take into account.
  ret

[GLOBAL writeBasePointer]
writeBasePointer:
  mov ebp, [esp+4]
  ret

[GLOBAL readBasePointer]
readBasePointer:
  mov eax, ebp
  ret

[GLOBAL writePageDirectory]
writePageDirectory:
  push ebp
  mov ebp, esp
  mov eax, [ebp+8]
  mov cr3, eax
  pop ebp
  ret

[GLOBAL readPageDirectory]
readPageDirectory:
  mov eax, cr3
  ret
